Skip to content

Instantly share code, notes, and snippets.

@robanonymous
Last active April 24, 2022 19:26
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save robanonymous/b677a47aed504548440c8258f71724b1 to your computer and use it in GitHub Desktop.
Save robanonymous/b677a47aed504548440c8258f71724b1 to your computer and use it in GitHub Desktop.
This is a custom hal_manualtoolchange file that allows you to control linuxcnc during a tool change. You can find the file in the folder /usr/bin.
#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys, os, time
import gettext
BASE = os.path.abspath(os.path.join(os.path.dirname(sys.argv[0]), ".."))
gettext.install("linuxcnc", localedir=os.path.join(BASE, "share", "locale"), unicode=True)
import linuxcnc, hal
linuxcncStat = linuxcnc.stat();
linuxcncCmd = linuxcnc.command()
"""
_after = None
def hal_in_background():
global _after
_after = None
if not h.change:
app.tk.call("set", "::tkPriv(button)", -1)
return
if (h.change_button):
h.changed = True
app.update()
app.tk.call("set", "::tkPriv(button)", -1)
stop_polling_hal_in_background()
return
_after = app.after(100, hal_in_background)
def poll_hal_in_background():
global _after
_after = app.after(100, hal_in_background)
def stop_polling_hal_in_background():
global _after
if _after: app.after_cancel(_after)
_after = None
"""
def do_change(n):
#Take the current line, remember, and install the program.......
linuxcncStat.poll()
# IMPORTANT - Before it was up 2.7 linuxcncStat.current_line
# But now it's value is somewhere in the strange lines, much more than the program stands now, towards the end of the file.
# It is even possible, if done "M6 T3" - something on the line where the actual call, and T3 is a change
# (such as he searches for the string in the code, and throws back ?! - that's the theory.)
curLine = linuxcncStat.motion_line
h.reported_current_line = curLine
# Previously, it had an abort() only if there is a current line. But why? Now we always abort(). :)
linuxcncCmd.abort()
if n:
message = _("Insert tool %d and click continue when ready") % n
else:
message = _("Remove the tool and click continue when ready")
# Close the dialog box (if the past is?)
app.wm_withdraw()
# Fulfill all the background tasks, is not made ?Tkinter? (see. below in the file, there is an explanation)
app.update()
# poll_hal_in_background ()
# Fuck, is this necessary? I do not understand. In fact it turned out, and it is not necessary, and so works ...
# try:
r = app.tk.call("nf_dialog", ".tool_change", _("Tool change"), message, "info", 0, _("Continue")) # show dialogue seems modal
# finally:
# stop_polling_hal_in_background()
# If `r` is a string, then convert it into an int. Apparently, app.tk.call () returns 0 if the call is a success
if isinstance(r, str):
r = int(r)
if r == 0:
if curLine >0:
h.commanded_run_from_line = curLine + 1
linuxcncCmd.mode(linuxcnc.MODE_AUTO)
linuxcncCmd.auto(linuxcnc.AUTO_RUN, curLine + 1)
app.update()
#=== CREATE PINS FOR LINUXCNC
h = hal.component("hal_manualtoolchange")
h.newpin("number", hal.HAL_S32, hal.HAL_IN)
h.newpin("commanded_run_from_line", hal.HAL_S32, hal.HAL_OUT) #This debug pin, we have created them - from hal-meter behind them can be seen :)
h.newpin("reported_current_line", hal.HAL_S32, hal.HAL_OUT) #This debug pin, we have created them - from hal-meter behind them can be seen :)
h.newpin("change", hal.HAL_BIT, hal.HAL_IN)
h.newpin("changed", hal.HAL_BIT, hal.HAL_OUT)
# See http://wiki.linuxcnc.org/cgi-bin/wiki.pl?Released_2.7.X
# Search strings hal_manualtool change.change button (version 2.7.0~pre3)
# h.newpin("change_button", hal.HAL_BIT, hal.HAL_IN)
# The component is ready.
# LinuxCNC when the component starts using "loadusr hal_manualtoolchange", apparently waiting for the UI.
h.ready()
#=== CREATE DIALOG WINDOW
import Tkinter, nf, rs274.options
app = Tkinter.Tk(className="AxisToolChanger")
app.wm_geometry("-60-60")
app.wm_title(_("AXIS Manual Toolchanger"))
rs274.options.install(app)
nf.start(app); nf.makecommand(app, "_", _)
"""
app.wm_protocol("WM_DELETE_WINDOW", app.wm_withdraw)
lab = Tkinter.Message(app, aspect=500, text = _("\
This window is part of the AXIS manual toolchanger. It is safe to close \
or iconify this window, or it will close automatically after a few seconds."))
lab.pack()
"""
"""
#=== CLOSE DIALOG WINDOW AFTER LINUXCNC STARTS
# Communication events <Expose> (http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/event-types.html) = the window is visible, the challenge of closing the window. That is, as soon as the window was visible from under another window, it must be closed immediately.
# Why it's done ?! And then, that the withdraw () function is called only once - see next.. line of code - when LinuxCNC starts.
# Apparently, this is some clever way to hide the startup dialog.
def withdraw():
app.wm_withdraw() # Removes the window from the screen (without destroying it)
app.bind("<Expose>", lambda event: app.wm_withdraw())
# After 10 seconds, after the start of the program (ie, start linuxcnc), hide the window automatically.
# This location is called only once, at startup.
app.after(10 * 1000, withdraw)
"""
#=== MAIN LOOP
try:
while 1:
# change = h.change
if h.change and not h.changed:
do_change(h.number)
h.changed = True
h.change = False
# 0.2 sec - wait, let the pins will update their status.
time.sleep(0.5)
h.changed = False
# elif not change:
# h.changed = False
# Why is this? What happens 0.1 seconds? Just the type of sleep?
# Call after () function without index - which gives delay type?
# app.after(100)
# This call performs all tasks that have Tkinter left unfinished.
# Apparently, the meaning is: to fulfill all that relies Tkinteru, and go to the next step while
# app.update()
except KeyboardInterrupt:
pass
@FastFarmer
Copy link

You are a champion! Thank you for this. I have it working with my probe setter for each tool change and am very happy.

@cptX
Copy link

cptX commented Nov 21, 2017

Hi guys I need to manually re-home Z axis in every tool change, so I need this functionality in any case and asap!
I tested with LinuxCNC 2.11 the script but is not working correctly.
What is happening is when the code meets Tx M6 (x is the number of the tool) the dialog box appears and the execution of the gcode is stopped. So far so good.
If you press continue in the dialog box the program is doing nothing, doesn't continue or anything.
I checked the code and should theoretically continue from the next line, but nothing is happening...
If I press the Play button again the code continues from the beginning...

Note: My tool change method is completely manual, there is no probe or other method to set the new Z, only with the eyes...

Any clues?

@cptX
Copy link

cptX commented Nov 21, 2017

I dug into the code and actually found out that both linuxcnc.stat().motion_line and linuxcnc.stat().current_line give wrong results.
I solved the problem by changing the line 52 from curLine = linuxcncStat.motion_line to curLine = linuxcncStat.read_line

According to http://linuxcnc.org/docs/2.6/html/common/python-interface.html
read_line: (returns integer) - line the RS274NGC interpreter is currently reading.

For me at version 2.7.11 works!!!

@frogandspanner
Copy link

This works, unless a tool change takes place in a subroutine. It looks as though the linuxcncCmd.abort() unwinds the call stack so destroys program flow.

@coke2k
Copy link

coke2k commented May 2, 2021

Just one stupid question: How to add this to AXIS GUI to make it work? How to run or where to copy the hal_manualtoolchange.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment